<?php
/**
 * WxPay.class.php
 * 微信支付
 *
 * @author  王中艺
 * @date    2016-11-15
 */

namespace Wechat\Util;

class WxPay {
    public  $apiUrl;
    public  $appId;
    public  $mchId;
    public  $mchKey;
    public  $notifyUrl;
    public  $transferUrl;
    public  $cert;
    public  $errMsg = false;

    function __construct(){
        $this->appId    = C('wechat.app_id');
        $this->mchId    = C('wechat_mch.id');
        $this->mchKey   = C('wechat_mch.key');
        $this->notifyUrl    = C('SERVER_DIR').'/index.php/Wechat/Public/wxPayNotify';
        $this->apiUrl   = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $this->transferUrl  = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
        $this->cert     = array(
            'ssl_cert'  => C('wechat_mch.ssl_cert'),
            'ssl_key'   => C('wechat_mch.ssl_key'),
            'ca_info'   => C('wechat_mch.ca_info'),
        );
    }

    /**
     * 发起支付
     * @param   $moneyId    交易ID
     * @param   $goodsName  商品名称
     * @param   $tradeNo    订单号
     * @param   $totalFee   支付金额
     * @param   $openId     用户open_id
     * @param   $tradeType  交易类型
     * @return  $payInfo
     */
    function createPay($moneyId, $goodsName, $tradeNo, $totalFee, $openId, $tradeType = 'JSAPI'){
        if(C('APP_STAGE') <= 2){
            $totalFee   = 1;
        }

        $nonceStr   = md5(time());
        $payInfo    = [
            'appid' => $this->appId,
            'mch_id'    => $this->mchId,
            'device_info'   => 'WEB',
            'nonce_str' => md5(time()),
            'body'  => $goodsName,
            'out_trade_no'  => $tradeNo,
            'total_fee' => $totalFee,
            'spbill_create_ip'  => get_client_ip(),
            'notify_url'    => $this->notifyUrl.'/'.think_encrypt($moneyId),
            'trade_type'    => $tradeType,
            'openid'    => $openId
        ];
        $payInfo['sign']    = $this->createSign($payInfo);
        $xmlPayInfo     = arr2xml($payInfo);
        $response   = $this->_sendRequest($this->apiUrl, $xmlPayInfo);
        $response   = simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA);
        $response   = json_decode(json_encode($response), true);

        if($response['result_code'] != 'SUCCESS'){
            write_log('./Log/wxpay_error.log', 'CREATE_TYPE_'.$tradeType, [
                'pay_info'  => $payInfo,
                'response'  => $response
            ]);
            throw new \Exception($response['return_msg']);
        }

        return array_merge($response, [
            'app_id'    => $this->appId,
            'nonceStr'  => $nonceStr,
            'signType'  => 'MD5'
        ]);
    }

    /**
     * 发起JSSDK支付
     * 留下兼用以前，新开发不再使用
     * @return [type] [description]
     */
    function createJsPay($payId, $goodsName, $tradeNo, $totalFee, $openId){
        if(C('APP_STAGE') <= 2){
            $totalFee   = 1;
        }

        $nonceStr   = date(YmdHis);
        $payInfo    = array(
            'appid' => $this->appId,
            'mch_id'    => $this->mchId,
            'device_info'   => 'WEB',
            'nonce_str' => $nonceStr,
            'body'  => $goodsName,
            'out_trade_no'  => $tradeNo,
            'total_fee' => $totalFee,
            'spbill_create_ip'  => get_client_ip(),
            'notify_url'    =>  $this->notifyUrl.'/'.think_encrypt($payId),
            'trade_type'    => 'JSAPI',
            'openid'   => $openId
        );
        ksort($payInfo);
        $payInfo['sign']   = $this->createSign($payInfo);
        $xmlPayInfo     = arr2xml($payInfo);
        
        $xmlRet     = do_curl($this->apiUrl, $xmlPayInfo);
        $odrInfo    = simplexml_load_string($xmlRet, 'SimpleXMLElement', LIBXML_NOCDATA);

        if($odrInfo->result_code != 'SUCCESS'){
            //订单发起错误
            write_log('./Log/wxpay_error.log', 'create_js_pay', $xmlRet);
            return false;
        }else{
            //JS支付信息生成
            $payInfo   = array(
                'appId' => C('wechat.app_id'),
                'timeStamp' => time(),
                'nonceStr'  => $nonceStr,
                'package'   => 'prepay_id='.$odrInfo->prepay_id,
                'signType'  => 'MD5'
            );
            $payInfo['paySign']    = $this->createSign($payInfo);
            return $payInfo;
        }
    }

    /**
     * 生成扫码支付连接
     * @param   $productId
     * @return  $url
     */
    function createScanPay($productId){
        $timeStamp  = time();
        $info   = [
            'appid' => $this->appId,
            'mch_id'   => $this->mchId,
            'time_stamp'    => $timeStamp,
            'nonce_str' => md5($timeStamp),
            'product_id'    => $productId
        ];

        $sign   = $this->createSign($info);
        $info['sign']   = $sign;

        return 'weixin://wxpay/bizpayurl?'.http_build_query($info);
    }
    /**
     * 签名生成
     * @param   $info
     * @return  $sign
     */
    public function createSign($info){
        ksort($info);
        $string     = '';
        foreach($info as $key => $val){
            $string     .= sprintf("%s=%s&", $key, $val);
        }
        $string     .= 'key='.$this->mchKey;
        $sign   = strtoupper(md5($string));
        return $sign;
    }

    /**
     * 向微信用户转账
     * @param   $openId     微信用户openid
     * @param   $amount     转账金额（分）
     * @param   $desc   描述
     */
    function transferToUser($openId, $amount, $desc){
        if(C('APP_STAGE') <= 2){
            $amount     = 100;
        }

        $tradeNo    = date('YmdHis').rand(1000, 9999);
        $nonceStr   = date(YmdHis);
        $transferInfo   = array(
            'mch_appid' => $this->appId,
            'mchid' => $this->mchId,
            'device_info'   => 'WEB',
            'nonce_str' => $nonceStr,
            'partner_trade_no'  => $tradeNo,
            'openid'    => $openId,
            'check_name'    => 'NO_CHECK',
            'amount'    => $amount,
            'desc'  => $desc,
            'spbill_create_ip'  => get_client_ip()
        );

        $transferInfo['sign']   = $this->createSign($transferInfo);
        $xmlInfo     = arr2xml($transferInfo);

        $xmlRet     = do_curl($this->transferUrl, $xmlInfo, $this->cert);
        $odrInfo    = simplexml_load_string($xmlRet, 'SimpleXMLElement', LIBXML_NOCDATA);

        if($odrInfo->result_code != 'SUCCESS'){
            //支付失败
            write_log('./Log/wxpay_error.log', 'create_js_pay', $xmlRet);
            $this->errMsg   = (string)$odrInfo->return_msg;
            return false;
        }

        return $odrInfo;
    }

    /**
     * 发送网络请求
     * @param   $url
     * @param   $data
     * @return  $response
     */
    private function _sendRequest($url, $data, $cert = false){
        $ch = curl_init(); 
        curl_setopt($ch, CURLOPT_URL, $url); 
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); 
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_AUTOREFERER, 1); 
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        //导入证书
        if($cert){
            curl_setopt($ch,CURLOPT_SSLCERT, $cert['ssl_cert']);
            curl_setopt($ch,CURLOPT_SSLKEY,  $cert['ssl_key']);
            curl_setopt($ch,CURLOPT_CAINFO,  $cert['ca_info']);
        }
        $response = curl_exec($ch);
        if(curl_errno($ch)){
            throw new \Exception(curl_error($ch));
        }
        curl_close($ch);

        return $response;
    }
}
